package com.ytktt.study.week2.task6;

import org.apache.hc.client5.http.ConnectTimeoutException;
import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.HttpRequestRetryStrategy;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.*;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.TimeValue;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * HttpClient5 请求 demo
 * http client 最新的大版本 正式版发布2年多，目前有漏洞警告
 * CVE-2021-45046/CVE-2021-44832/CVE-2021-44832/CVE-2021-44228/CVE-2020-15250
 * 可以看出v5先对v4，很多api的定义更加亲和，但在本例中调用相比v4慢了许多
 *
 * @author ytk
 */
public class HttpClient5Demo {

    public static void main(String[] args) throws Exception {

        try (CloseableHttpClient httpClient = HttpClients.custom()
                // 自定义 keepalive 策略
                .setKeepAliveStrategy(getConnectionKeepAliveStrategy())
                // 添加自定义请求重试处理器
                .setRetryStrategy(getHttpRequestRetryStrategy())
                // 设置连接管理器
                .setConnectionManager(getPoolingHttpClientConnectionManager())
                // 添加自定义请求拦截器
                .addRequestInterceptorLast(getHttpRequestInterceptor())
                .build()) {
            RequestConfig requestConfig = RequestConfig.custom()
                    // 设置 socket 超时时间（请求服务器传输数据的事件）
                    .setResponseTimeout(3, TimeUnit.SECONDS)
                    // 设置连接超时时间（请求服务器发起建立连接的时间）
                    .setConnectTimeout(3, TimeUnit.SECONDS)
                    .build();

            HttpGet httpGet = new HttpGet("http://localhost:8081");
            httpGet.setConfig(requestConfig);

            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                if (HttpStatus.SC_OK == response.getCode()) {
                    HttpEntity httpEntity = response.getEntity();
                    String contentType = httpEntity.getContentType();
                    if (contentType.contains("text/html")) {
                        String content = EntityUtils.toString(httpEntity, StandardCharsets.UTF_8);
                        System.out.println(content);
                    }
                    // 处理些其他的格式例如 application/json
                } else {
                    System.out.println("请求发生错误: " + response.getCode());
                }
            }
        }
    }

    @NotNull
    private static ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
        return new DefaultConnectionKeepAliveStrategy() {
            @Override
            public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context) {
                TimeValue keepAlive = super.getKeepAliveDuration(response, context);
                // 源码中已经默认设置了3min
                return keepAlive;
            }
        };
    }

    @NotNull
    private static HttpRequestRetryStrategy getHttpRequestRetryStrategy() {
        return new HttpRequestRetryStrategy() {
            @Override
            public boolean retryRequest(HttpRequest request, IOException exception, int execCount, HttpContext context) {
                if (execCount >= 5) {
                    // 重试次数不能大于等于5次
                    return false;
                }
                if (exception instanceof InterruptedIOException) {
                    // IO 中断不再尝试
                    return false;
                }
                if (exception instanceof UnknownHostException) {
                    // 找不到域名 不再尝试
                    return false;
                }
                if (exception instanceof ConnectTimeoutException) {
                    // 连接超时再次尝试
                    return true;
                }
                if (exception instanceof SSLException) {
                    // SSL 握手失败再次重试
                    return true;
                }
                boolean idempotent = true;
                if (idempotent) {
                    // 可以根据request判断请求是否是幂等的，例如get/delete/put方法
                    return true;
                }
                return false;
            }

            @Override
            public boolean retryRequest(HttpResponse response, int execCount, HttpContext context) {
                return execCount < 5;
            }

            @Override
            public TimeValue getRetryInterval(HttpResponse response, int execCount, HttpContext context) {
                final Header header = response.getFirstHeader(HttpHeaders.RETRY_AFTER);
                TimeValue retryAfter = null;
                if (header != null) {
                    final String value = header.getValue();
                    try {
                        retryAfter = TimeValue.ofSeconds(Long.parseLong(value));
                    } catch (final NumberFormatException ignore) {
                        final Date retryAfterDate = DateUtils.parseDate(value);
                        if (retryAfterDate != null) {
                            retryAfter = TimeValue.ofMilliseconds(retryAfterDate.getTime() - System.currentTimeMillis());
                        }
                    }

                    if (TimeValue.isPositive(retryAfter)) {
                        return retryAfter;
                    }
                }
                return TimeValue.ofSeconds(2L);
            }
        };
    }

    @NotNull
    private static PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager() {
        // 池化管理连接
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
        // 设置连接池的最大连接数 默认 20
        manager.setMaxTotal(200);
        // 每个请求最多的并发量 默认2
        manager.setDefaultMaxPerRoute(32);
        return manager;
    }

    @NotNull
    private static HttpRequestInterceptor getHttpRequestInterceptor() {
        return new HttpRequestInterceptor() {
            private final AtomicInteger count = new AtomicInteger(0);
            @Override
            public void process(HttpRequest httpRequest, EntityDetails entityDetails, HttpContext httpContext) throws HttpException, IOException {
                // 请求计数
                httpRequest.addHeader("X-Request-Count", Integer.toString(count.getAndIncrement()));
                // 还可以记录一些日志...
            }
        };
    }
}
